
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Gui;
using Swaf.Security;

using Nitobi.Components.Model;

[assembly: WebResource("Syrinx.Gui.AspNet.NitobiCtls.nitobi.grid.v3.js", "application/x-javascript")]
[assembly: WebResource("Syrinx.Gui.AspNet.NitobiCtls.ExtendedGridSupport.js", "application/x-javascript")]

namespace Syrinx.Gui.AspNet.NitobiCtls
{
	[ParseChildren(false)]
	public class NitobiBaseGridBizObjListDisplay : Swaf.Gui.AspNet2.UserControl, INamingContainer, IGridBizObjListDisplay, IWidget, INitobiClientControl
	{
		protected static int s_bizObjKeyRowIndex = 0;
		public static int BizObjKeyRowIndex { get { return s_bizObjKeyRowIndex; } }
		protected static XmlDocument s_columnInitInfo;
		protected static XmlNamespaceManager s_nsmgr;
		static NitobiBaseGridBizObjListDisplay()
		{
			s_columnInitInfo = new XmlDocument();
			s_columnInitInfo.LoadXml(@"
<ntb:columns xmlns:ntb=""http://www.nitobi.com"">
<ntb:checkboxeditor datasource=""[{value:'True',display:'T'},{value:'False',display:'N'}]"" checkedvalue=""True"" uncheckedvalue=""False"" displayfields=""display"" valuefield=""value""/>
</ntb:columns>
");
			s_nsmgr = new XmlNamespaceManager(s_columnInitInfo.NameTable);
			s_nsmgr.AddNamespace("ntb", "http://www.nitobi.com");
		}

		protected internal Nitobi.Grid m_gridDisplay;
		protected HiddenField m_scrollInfoField, m_selectedRowInfoField;

		protected Privileges m_editPriv = Privileges.ReadOnly;
		protected int m_maxElements = -1;
		protected XmlElement m_layoutTemplate = null;
		protected bool m_enableMultiRowSelect = true;
		protected bool m_toolbarEnable = true;
		protected string m_selectedRowInfo; //Note this is used over m_selectedRowInfoField when doing an ajax postback and it comes from state
		protected bool m_mySelectEvent = false;
		protected bool m_eventNeedsRefresh = false;
		protected bool m_liveScroll = false;
		protected int m_frozenCount = -1;
		protected bool m_processedLayout = false;

		protected IBizObjList m_bizobjList;
		protected string m_clientBizObjMods = null;
		protected bool m_processedClientBizObjModes = false;

		protected string m_dataSourceBizRule = null;
		protected object m_dataSourceBizRuleParam = null;
		protected bool m_setupGrid = false;


		public event BizObjChanged BizObjAdded;

		public event BizObjChanged BizObjDeleted;

		public event BizObjSelectedInfo BizObjSelected;

		public event BizObjChanged BizObjUpdated;

		public event BizProcEvent UpdateNotificationComplete;

		public NitobiBaseGridBizObjListDisplay()
			:base()
		{
		}

		public NitobiBaseGridBizObjListDisplay(ActiveWidget widget)
			:base(widget)
		{
		}
		#region Public Control Properties

		public Privileges EditPrivilege {get{return m_editPriv;}set{m_editPriv = value;}}
		public int MaxElements {get{return m_maxElements;} set{m_maxElements = value;} }
		public bool EnableToolbar { get { return m_toolbarEnable; } set { m_toolbarEnable = value; } }
		public bool LiveScroll { get { return m_liveScroll; } set { m_liveScroll = value; } }
		public int FrozenLeftColumns { get { return m_frozenCount; } set { m_frozenCount = value; } }
		#endregion

		#region IBizObjListDisplay2 Members
		public string DataSourceBizRule { get { return null; } set { } }
		public object DataSourceBizRuleParam { get { return null; } set { } }
		#endregion

		public IBizObj SelectedBizObj
		{
			get
			{
				string[] rowIds = SelectedRowIds;
				if (m_bizobjList != null && rowIds.Length > 0)
				{
					int index = int.Parse(rowIds[0]);
					if(index >= m_bizobjList.numElements)
						index = m_bizobjList.numElements -1;
					return m_bizobjList[index];
				}
				return null;

			}
			set
			{
				if (value == null)
				{
					m_selectedRowInfoField.Value = m_selectedRowInfo = "";
				}
				else if (value.isNew)
				{
					for (int p = m_bizobjList.numElements - 1; p >= 0; p--)
						if (m_bizobjList[p] == value)
						{
							SelectedRowIds = new string[] { p.ToString() };
							break;
						}

				}
				else
				{
					for (int p = 0; p < m_bizobjList.numElements; p++)
						if (m_bizobjList[p] == value)
						{
							SelectedRowIds = new string[] { p.ToString() };
							break;
						}
				}
			}
		}

		public object SelectedBizObjKeyValue
		{
			get
			{
				IBizObj bizobj = SelectedBizObj;
				return (bizobj != null) ? bizobj.KeyValue : null;
			}
			set 
			{
				SelectedRowIds = value == null ? new string[] { } : new string[] { value.ToString() };
			} 
		}

		public void getSelectedBizObjs(IBizObjList selected)
		{
			if (m_bizobjList != null)
				foreach (string index in SelectedRowIds)
					selected.Add(m_bizobjList[Int32.Parse(index)]);
		}
		public void setSelectedBizObjs(IBizObjList selected)
		{
		}

		public virtual void updateBizObjs()
		{
			//TODO: This should look for changes made in the grid by the user when the grid is not automatically
			//sending changes up to the server after every row is changed.
		}

		public void showBizObjs(IBizObjCursor cur)
		{
			if (Page != null)
			{
				setupGrid();
				m_bizobjList = cur.Source;
				processLayoutTemplate();
				string parentId = State.ParentBizProcIds[0];
				if (!LiveScroll)
				{
					Nitobi.Components.Model.DataSource ds = m_gridDisplay.DataSources[parentId];
					if (ds == null)
					{
						ds = m_gridDisplay.DataSources.AddNewDataSource(parentId);
						ds.DatasourceStructure.Keys.Add("id");
						string[] fieldNames = new string[m_gridDisplay.Columns.Count];
						for (int pos = 0; pos < m_gridDisplay.Columns.Count; ++pos)
							fieldNames[pos] = m_gridDisplay.Columns[pos].DataField;
						ds.FieldNames = fieldNames;
					}
					ds.Source = m_bizobjList;
					ds.DataBind();
				}
				processClientMods();
			}
		}

		//public string ColumnNames
		//{
		//    get
		//    {
		//        if (m_layoutTemplate == null) return "";
		//        processLayoutTemplate();
		//        StringBuilder buff = new StringBuilder();
		//        foreach (XmlNode node in m_layoutTemplate.ChildNodes)
		//            if (node is XmlElement && "column".CompareTo(node.Name.ToLower()) == 0)
		//                buff.Append(((XmlElement)node).GetAttribute("field")).Append("|");
		//        return buff.ToString(0, buff.Length - 1);
		//    }
		//}

		public string ColumnNames
		{
			get
			{
				setupGrid();
				processLayoutTemplate();
				StringBuilder buff = new StringBuilder();
				int numCols = m_gridDisplay.Columns.Count;
				for (int p = 0; p < numCols; p++)
					buff.Append(m_gridDisplay.Columns[p].DataField).Append("|"); ;
				return buff.ToString(0, buff.Length - 1);
			}
		}

		protected IApplication App { get { return Application.currentApp; } }


		protected void processLayoutTemplate()
		{
			if (!m_processedLayout && m_layoutTemplate != null && m_gridDisplay != null)
			{
				ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);

				foreach (XmlNode node in m_layoutTemplate.ChildNodes)
					if (node is XmlElement)
					{
						m_processedLayout = true;
						XmlElement child = (XmlElement)node;
						string colType = child.Name.ToLower();
						if ("column".CompareTo(colType) == 0)
							createGridColumn(child, bizproc);
						else if("columngroup".CompareTo(colType) == 0)
							createGridColumnGroup(child, bizproc);
					}
			}
		}

		public XmlElement LayoutTemplate
		{
			get
			{
				throw new Exception("The method or operation is not implemented.");
			}
			set
			{
				m_layoutTemplate = value;
			}
		}

		/// <summary>
		/// Assumes that child is not null and is a "columnGroup" type xmlelement.
		/// </summary>
		/// <remarks>
		/// 
		/// The following example shows a columnGroup specification.  The columnGroup
		/// rule attribute specifies a bizrule that should return a bizobjlist where
		/// each bizobj will help define one or more columns for the grid.  The columnGroup
		/// can have one or more column child elements.  For each bizobj in the bizobjlist
		/// the set of columns defined in the columnGroup will be created.
		/// 
		/// Note that the child column elements use deep name address names for the label and field
		/// attributes.  So, in the example below, the label of the column for each room
		/// type will not be "name" but will get a call to the roomType.get("name") and the
		/// value of that for the given room type will be used as the label. 
		/// 
		/// The label attribute is used against the bizobjs returned by the columngroup
		/// bizrule.
		/// 
		/// The field attribute is used against each of the bizobjs being bound to for the 
		/// grid to display as a normal field will.  The field value should have one {0}
		/// contained in it, which will be replaced with the key value of the given
		/// column group bizobj being bound to.
		/// <code>
		/// <columnGroup rule="getRoomTypes">
		///		<column label="name" field="roomTypeAllocations.{0}.quantity"
		/// </columnGroup>
		/// </code>
		/// 
		/// In the example above, if te getRoomTypes bizrule returned 3 roomtype bizobjs with
		/// key values of "a", "b", and "c", there would be 3 columns added where the columnGroup
		/// was placed relative to other columns in the grid.  When the grid is then bound
		/// to its data bizobjlist,it will bind to the field attribute of the 3 columns created.  The field will bind
		/// into each of the bizobjs in the bound list using the deep name.  For the example above,
		/// the bound data is a list of "booking agents" that have room allocations for each
		/// of the room types.  Each booking agent bizobj would have the 3 columns binding as follows:
		/// 
		/// roomTypeAllocations.a.quantity
		/// roomTypeAllocations.b.quantity
		/// roomTypeAllocations.c.quantity
		/// 
		/// </remarks>
		/// <param name="child"></param>
		/// <param name="bizproc"></param>
		protected void createGridColumnGroup(XmlElement child, ActiveBizProc bizproc)
		{
			string ruleName = child.GetAttribute("rule");
			bool useKeyField = true;
			string idField = child.GetAttribute("idField");
			if (!Cmn.IsEmpty(idField))
				useKeyField = false;

			if (!Cmn.IsEmpty(ruleName))
			{
				IResults rc = App.ruleMgr.execute(ruleName);
				IBizObjList columns = rc.CallResults as IBizObjList;
				if(columns != null)
					foreach (IBizObj colBizObj in columns)
						foreach (XmlNode node in child.ChildNodes)
							if (node is XmlElement)
								if ("column".CompareTo(node.Name.ToLower()) == 0)
								{
									XmlElement el = (XmlElement)node.Clone();
									try
									{
										string replVal = useKeyField ? colBizObj.KeyValue.ToString() : colBizObj.get(idField).ToString();
										el.SetAttribute("label", colBizObj.get(el.GetAttribute("label")).ToString());
										el.SetAttribute("field", string.Format(el.GetAttribute("field"), replVal));
									}
									catch(Exception)
									{
										//Report back a failure while setting up the fields
									}
									createGridColumn(el, bizproc);
								}
			}

		}

		/// <summary>
		/// Assumes that child is not null and is a "column" type xmlelement.
		/// </summary>
		/// <param name="child"></param>
		/// <param name="bizproc"></param>
		protected void createGridColumn(XmlElement child, ActiveBizProc bizproc)
		{
			string fieldName = child.GetAttribute("field");
			string label = child.GetAttribute("label");
			int width = 0;
			int.TryParse(child.GetAttribute("width"), out width);
			string editor = child.GetAttribute("editor");
			bool visible = string.Compare(child.GetAttribute("visible"), "false", true) != 0;


			Nitobi.Components.Grid.Column cl = null;
			string lowEditor = editor.ToLower();
			if (lowEditor.StartsWith("calendar"))
			{
				Regex r = new Regex(@"calendar\((.*?)\)", RegexOptions.IgnoreCase);
				Match m = r.Match(editor);
				if (m.Success)
				{
					Nitobi.Components.Grid.DateColumn dc = new Nitobi.Components.Grid.DateColumn();
					dc.Mask = m.Groups[1].Value;
					Nitobi.Components.Grid.DateEditor de = new Nitobi.Components.Grid.DateEditor();
					de.Mask = m.Groups[1].Value;
					dc.Editor = de;
					cl = dc;
				}
				else
					throw new BaseException("Bad grid column editor format:" + editor);
			}
			else if (string.Compare(editor, "number", true) == 0)
			{
				Nitobi.Components.Grid.NumberColumn nc = new Nitobi.Components.Grid.NumberColumn();
				if (child.HasAttribute("mask"))
					nc.Mask = child.GetAttribute("mask");
				cl = nc;
			}
			else
			{
				cl = new Nitobi.Components.Grid.TextColumn();

				if (string.Compare(editor, "checkbox", true) == 0)
				{
					cl.Editor = new Nitobi.Components.Grid.CheckboxEditor(s_columnInitInfo.DocumentElement.SelectSingleNode("ntb:checkboxeditor", s_nsmgr));
				}
				else if (lowEditor.StartsWith("dropdown("))
				{
					Regex r = new Regex(@"dropdown\((.*?),(.*?),(.*?)\)", RegexOptions.IgnoreCase);
					Match m = r.Match(editor);
					if (m.Success)
					{
						string br = m.Groups[1].Value;
						Regex r2 = new Regex(@"(.*?)\[(.*?)\]", RegexOptions.IgnoreCase);
						Match m2 = r2.Match(br);
						if (m2.Success)
						{
							string brParam = m2.Groups[2].Value;
							if (string.Compare(brParam, "ParentBizProcId", true) == 0)
								brParam = bizproc.OfflineState.Id;
							else if (string.Compare(brParam, "ParentParentBizProcId", true) == 0)// && m_parentBizProc != null && m_parentBizProc.ParentBizProc != null)
								brParam = bizproc.OfflineState.Id;
							br = string.Format("{0}[{1}]", m2.Groups[1].Value, brParam);
						}

						Nitobi.Components.Grid.ListboxEditor l = new Nitobi.Components.Grid.ListboxEditor();
						string url = string.Format("/NitiboLiveStreamHandler.aspx?pi={0}&br={1}&cl={2}|{3}", BPC.PageState.PageId, br, m.Groups[2].Value, m.Groups[3].Value);
						url = Page.Response.ApplyAppPathModifier(url);
						l.GetHandlerUrl = url;
						l.ValueField = m.Groups[2].Value;
						string[] df = m.Groups[3].Value.Split('|');
						foreach (string f in df)
							l.DisplayFields.Add(f);
						cl.Editor = l;
					}
					else
						throw new BaseException("Bad grid column editor format:" + editor);
				}
			}
			cl.DataField = fieldName;
			cl.Label = label;
			cl.Width = width;
			cl.Visible = visible;
			cl.Editable = editor != "";
			cl.SortEnabled = false;
			m_gridDisplay.Columns.Add(cl);
		}

		public string RefreshListScript 
		{ get { return string.Format("nitobi.getGrid('{0}').refresh();", m_gridDisplay.ClientID, Width.Value, Height.Value); } }

		public string getRefreshAndSetSelectedScript(int selectedRow)
		{
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			string mainId = UniqueID.Replace("$", "").Replace("_","");
			string gridId = m_gridDisplay.ClientID;
			if (gridId.CompareTo("gr") == 0)
				gridId = ClientID + "_" + gridId;
			if (LiveScroll)
				return string.Format(@"
var g = nitobi.getComponent('{0}');
g.subscribeOnce('AfterRefresh', 
function()
{{
	var g = nitobi.getGrid('{0}');
	g.clearActiveRows();
	g.finalizeRowCount({3});
	//g.datatable.get({2}, 20);
	if({2} != -1)
		setTimeout('var g = nitobi.getGrid(\'{0}\');g.selectGridRow({2}, true);',80);
//		setTimeout('var g = nitobi.getGrid(\'{0}\');g.ensureCellInView(g.getCellElement({2}, 1);',0);
}}
,null, new Array());
g.rowCountKnown = false;
g.clearSurfaces();
g.dataBind();
g.refresh();",
										gridId, mainId, selectedRow, m_bizobjList==null?1000:m_bizobjList.numElements);
			else
				return string.Format(@"
var xml = '{4}';
var g = nitobi.getComponent('{0}');
if(g != null)
{{
var ds = g.getDataSource('{3}');
ds.initializeXml(xml);
g.subscribeOnce('AfterRefresh', 
function()
{{
	var g = nitobi.getGrid('{0}');
	g.clearActiveRows();
	g.selectGridRow({2}, true);
	rowSelected{1}();
}}
,null, new Array());
g.refresh();
}}",
										gridId, mainId, selectedRow, bizproc.OfflineState.Id, getGridXml(null, false, 0, -1).Replace("'",@"\'"));
		}

		protected string getGridXml(string bizRule, bool comboStyle, int startIndex, int numRows)
		{
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			string namespaceUri = "http://www.nitobi.com";
			XmlDocument doc = new XmlDocument();
			XmlElement dataEl = null;
			string columnNames = ColumnNames;
			string[] columnNameArray = columnNames.Split('|');


			IBizObjList bizobjs = null;

			if (bizRule != null)
				bizobjs = Swaf.Application.currentApp.ruleMgr.execute(bizRule).CallResults as IBizObjList;
			else
				bizobjs = this.m_bizobjList;

			if (!comboStyle)
			{
				doc.LoadXml(string.Format(@"
<ntb:grid xmlns:ntb='http://www.nitobi.com'><ntb:datasources><ntb:datasource id='{0}'></ntb:datasource></ntb:datasources></ntb:grid>", bizproc.OfflineState.Id));
				XmlElement dsEl = doc.DocumentElement.ChildNodes[0].ChildNodes[0] as XmlElement;
				XmlElement dssEl = doc.CreateElement("ntb", "datasourcestructure", namespaceUri);
				dsEl.AppendChild(dssEl);
				dssEl.SetAttribute("fieldnames", columnNames); //string.Join("|", active.ColumnNames));
				dataEl = doc.CreateElement("ntb", "data", namespaceUri);
				dsEl.AppendChild(dataEl);
			}
			else
			{
				string baseXml = String.Format("<root fields='{0}' keys='{0}' xml:lang='en' types='' />", columnNames);
				doc.LoadXml(baseXml);
				dataEl = doc.DocumentElement;
				namespaceUri = null;
			}

			if (bizobjs != null)
				for (int pos = startIndex; pos < bizobjs.numElements && ((numRows <= 0) ? true : pos < (pos + numRows)); ++pos)
				{
					XmlElement eEl = doc.CreateElement("ntb", "e", namespaceUri);
					char aName = 'a';
					foreach (string name in columnNameArray)
						eEl.SetAttribute((aName++).ToString(), bizobjs[pos].get(name).ToString());

					if (!comboStyle)
						eEl.SetAttribute("xi", pos.ToString());
					else
					{
						eEl.SetAttribute("fld", "");
						eEl.SetAttribute("xk", (pos + 1).ToString());
					}
					dataEl.AppendChild(eEl);
				}
			string xml = doc.OuterXml;
			return xml;
		}

		protected override void AddParsedSubObject(object obj)
		{
			System.Web.UI.LiteralControl control = (System.Web.UI.LiteralControl)obj;
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(control.Text);
			this.LayoutTemplate = doc.DocumentElement;
		}

		protected override void OnPreRender(EventArgs e)
		{
			if (m_layoutTemplate == null)
				return;
			base.OnPreRender(e);
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			processLayoutTemplate();
			string mainId = UniqueID.Replace("$", "");

			Page.ClientScript.RegisterClientScriptBlock(typeof(NitobiBaseGridBizObjListDisplay), "PerGrid" + UniqueID,
				string.Format(s_perGridScript, mainId, m_gridDisplay.ClientID, m_scrollInfoField.ClientID, m_selectedRowInfoField.ClientID,
				bizproc.OfflineState.Id, ClientID, m_layoutTemplate.OuterXml.Replace("'", "\\'"), LiveScroll.ToString().ToLower(), m_bizobjList==null?1000:m_bizobjList.numElements),
				true);

			string scriptUrl = Page.ClientScript.GetWebResourceUrl(typeof(NitobiBaseGridBizObjListDisplay), "Syrinx.Gui.AspNet.NitobiCtls.nitobi.grid.v3.js");
			Page.ClientScript.RegisterClientScriptInclude("Nitobi.GridScript", scriptUrl);
			scriptUrl = Page.ClientScript.GetWebResourceUrl(typeof(NitobiBaseGridBizObjListDisplay), "Syrinx.Gui.AspNet.NitobiCtls.ExtendedGridSupport.js");
			Page.ClientScript.RegisterClientScriptInclude("ExtendedGridSupport", scriptUrl);
		}

		protected override void OnLoad(EventArgs e)
		{
			{
				setupGrid();
				base.OnLoad(e);
			}
		}

		protected override void Render(HtmlTextWriter writer)
		{
			if (m_layoutTemplate == null)
				return;
			base.Render(writer);
		}

		protected void setupGrid()
		{
			if (!m_setupGrid)
			{
				m_setupGrid = true;
				if (m_createOnLoad)
				{
					WidgetOfflineState offlineState = State;
					ActiveWidget act = new ActiveWidget(this, BPC, offlineState);
					BPC.defineActiveWidget(act);
					offlineState.State.put("liveScroll", m_liveScroll);
					offlineState.State.put("layoutTemplate", m_layoutTemplate);
				}
				else
				{
					m_layoutTemplate = (XmlElement)State.State.get("layoutTemplate");
					m_liveScroll = (bool)State.State.get("liveScroll");
				}
				if (m_scrollInfoField != null || Page == null)
					return;

				base.CreateChildControls();
				m_scrollInfoField = new HiddenField();
				m_scrollInfoField.ID = "si";
				Controls.Add(m_scrollInfoField);
				m_scrollInfoField.Value = Page.Request.Form[m_scrollInfoField.UniqueID];

				m_selectedRowInfoField = new HiddenField();
				m_selectedRowInfoField.ID = "ri";
				Controls.Add(m_selectedRowInfoField);
				m_selectedRowInfoField.Value = Page.Request.Form[m_selectedRowInfoField.UniqueID];

				m_gridDisplay = new Nitobi.Grid();
				m_gridDisplay.ID = "gr";
				m_gridDisplay.ToolbarEnabled = EnableToolbar;
				Controls.Add(m_gridDisplay);
				m_gridDisplay.Width = Width;// new Unit("500px");
				m_gridDisplay.Height = Height;// new Unit("300px");
				m_gridDisplay.RowSelectEnabled = "True";
				m_gridDisplay.Style.Clear();
				m_gridDisplay.Theme = "Vista";
				m_gridDisplay.AutoSpecifyDataHandlerUrls = false;
				m_gridDisplay.MultiRowSelectEnabled = m_enableMultiRowSelect ? "True" : "False";
				m_gridDisplay.OnCellFocusEvent = "gridCellFocus(eventArgs)";
				m_gridDisplay.OnAfterCellEditEvent = "gridCellEditComplete(eventArgs)";
				//m_gridDisplay.OnCellBlurEvent = "gridCellEditComplete(eventArgs)";
				string mainId = UniqueID.Replace("$", "");
				string rowFocusEvent = string.Format("rowSelected{0}()", mainId);
				m_gridDisplay.OnCellClickEvent = m_gridDisplay.OnRowFocusEvent = rowFocusEvent;
				m_gridDisplay.OnHtmlReadyEvent = string.Format("setupGridState{0}('{1}')", mainId, m_gridDisplay.ClientID);
				if (m_frozenCount > -1)
					m_gridDisplay.FrozenLeftColumnCount = m_frozenCount;
				setupGridAjaxUrl();

				if (m_layoutTemplate != null)
					LayoutTemplate = m_layoutTemplate;

				Page.ClientScript.RegisterStartupScript(GetType(), "AutoInit",
					@"
					function initGridsWait() {
					try { if(window.attachEvent){window.attachEvent('onload', initEBAGrids);}else{window.addEventListener('load',initEBAGrids,false);} } 
					catch(e) {setTimeout('initGridsWait()',100);}
					}initGridsWait();",
					true);
			}
		}

		protected void setupGridAjaxUrl()
		{
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			if (m_gridDisplay != null)
				if (!LiveScroll)
				{
					m_gridDisplay.Mode = Nitobi.Grid.ModeType.LocalNonPaging;
					m_gridDisplay.DataSourceId = bizproc.OfflineState.Id;
				}
				else
				{
					m_gridDisplay.Mode = Nitobi.Grid.ModeType.LiveScrolling;
					string url = string.Format(s_ajaxUrl, BPC.PageState.PageId, bizproc.OfflineState.Id, ColumnNames);
					url = Page.Response.ApplyAppPathModifier(url);
					m_gridDisplay.GetHandlerUrl = url;
					m_gridDisplay.SaveHandlerUrl = url;
				}
		}

		protected string[] SelectedRowIds
		{
			get
			{
				setupGrid();
				if (m_selectedRowInfoField != null && m_selectedRowInfoField.Value != "" && (m_selectedRowInfo == null || m_selectedRowInfo == ""))
					m_selectedRowInfo = m_selectedRowInfoField.Value;

				string[] rowIds = (m_selectedRowInfo == null || m_selectedRowInfo == "") ? new string[0] : m_selectedRowInfo.Split(',');
				return rowIds;
			}
			set
			{
				//setupGrid();
				if(m_selectedRowInfoField != null)
					m_selectedRowInfoField.Value = m_selectedRowInfo = string.Join(",", value);
			}
		}

		#region INitobiClientControl Members
		public bool NeedToGenerateHtmlWithServerControls { get { return false; } }

		public void setState(XmlElement clientState)
		{
			ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
			m_selectedRowInfo = clientState.GetAttribute("selectedRows");
			string ajaxEvent = clientState.OwnerDocument.DocumentElement.GetAttribute("event").ToLower();

			bool mybizProc = clientState.OwnerDocument.DocumentElement.GetAttribute("bizProcId").CompareTo(bizproc.OfflineState.Id) == 0;
			if (ajaxEvent.CompareTo("bizobjselected") == 0 && mybizProc)
				m_mySelectEvent = true;
			else// if (mybizProc)
				m_eventNeedsRefresh = true;
			string txt = clientState.GetAttribute("xt");
			if (txt != null && txt != "")
			{
				XmlDocument doc = new XmlDocument();
				doc.LoadXml(txt);
				m_layoutTemplate = doc.DocumentElement;
			}
			if (clientState.HasAttribute("ls"))
				LiveScroll = bool.Parse(clientState.GetAttribute("ls"));
		
			//if (m_parentBizProc != null && m_parentBizProc.SourceBizObjs != null)
			//	m_parentBizProc.useData(m_parentBizProc.SourceBizObjs);
			string updatesXml = clientState.GetAttribute("updates");
			if (updatesXml != null && updatesXml != "")
			{
				m_mySelectEvent = false;
				m_clientBizObjMods = updatesXml;
				processClientMods();
				clientState.RemoveAttribute("updates");
			}
		}

		public void getState(XmlElement clientState, string pageHtml)
		{
			if (!m_mySelectEvent)
			{
				int selrow = 0;
				string[] selrows = SelectedRowIds;
				if (selrows != null && selrows.Length != 0)
				{
					string strkey = selrows[selrows.Length - 1];
					if (m_bizobjList == null)
					{
						ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
						m_bizobjList = bizproc.BizProc.SourceBizObjs;
					}
					object key = m_bizobjList.objectType.convertValueToKeyType(strkey);
					for (selrow = 0; selrow < m_bizobjList.numElements; selrow++)
						if (m_bizobjList[selrow].KeyValue.Equals(key))
							break;
				}
				if(m_eventNeedsRefresh)
					clientState.SetAttribute("refresh", getRefreshAndSetSelectedScript(selrow));
				if (LiveScroll && m_bizobjList != null)
					clientState.SetAttribute("rowCount", m_bizobjList.numElements.ToString());
			}
		}

		protected virtual void 
			
			processClientMods()
		{
			if (m_bizobjList != null && m_clientBizObjMods != null && !m_processedClientBizObjModes)
			{
				ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
				XmlDocument d = new XmlDocument();
				d.LoadXml(m_clientBizObjMods);
				m_clientBizObjMods = null;
				m_processedClientBizObjModes = true;
				XmlNamespaceManager nsmgr = new XmlNamespaceManager(d.NameTable);
				nsmgr.AddNamespace(Nitobi.XmlNamespace.Prefix, Nitobi.XmlNamespace.Uri);
				Nitobi.Grid grid = new Nitobi.Grid();
				Nitobi.Components.Model.DataSource ds = grid.DataSources.AddNewDataSource(bizproc.OfflineState.Id);

				XmlNode dsNode = d.SelectSingleNode("//ntb:datasource[@id='" + bizproc.OfflineState.Id + "']/ntb:data", nsmgr);
				if (dsNode == null)
					dsNode = d.SelectSingleNode("//ntb:datasource[@id='_default']/ntb:data", nsmgr);
				if (dsNode != null)
				{
					ds.Rows.DeserializeFromXml(dsNode.OuterXml);
					IBizObjList mainList = bizproc.BizProc.SourceBizObjs;
					IBizObjList modified = App.bizObjMgr.createList(mainList.objectType.Name);

					foreach (Nitobi.Components.Model.Row row in ds.Rows)
					{
						string rowId = row.Id;
						int rowIndex = int.Parse(rowId);
						IBizObj modifiedBizObj = null;
						if (modified.objectType.KeyFields.Count > 0)
						{
							string bizobjKeyString = row[NitobiBaseGridBizObjListDisplay.BizObjKeyRowIndex];
							object bizobjKeyValue = modified.objectType.convertValueToKeyType(bizobjKeyString);
							modifiedBizObj = m_bizobjList.search(modified.objectType.KeyFieldNames, bizobjKeyValue);
						}
						else
							modifiedBizObj = m_bizobjList[rowIndex];
						if (modifiedBizObj == null)
							modifiedBizObj = App.bizObjMgr.create(mainList.objectType.Name);

						Nitobi.Components.Model.UpdateAction xac = row.EditAction;
						string[] columnNames = ColumnNames.Split('|');
						switch (xac)
						{
							case (UpdateAction.INSERT):
								{
									for (int p = 0; p < columnNames.Length; p++)
										if (string.Compare(covertToString(modifiedBizObj.get(columnNames[p])), row[p]) != 0)
											modifiedBizObj.put(columnNames[p], row[p]);
									bizproc.BizProc.updateBizObj(modifiedBizObj);
									break;
								}
							case (UpdateAction.DELETE):
								{
									modifiedBizObj.isNew = false;
									modifiedBizObj.isDeleted = true;
									bizproc.BizProc.deleteBizObj(modifiedBizObj.KeyValue);
									break;
								}
							case (UpdateAction.UPDATE):
								{
									for (int p = 0; p < columnNames.Length; p++)
										if (string.Compare(covertToString(modifiedBizObj.get(columnNames[p])), row[p]) != 0)
											modifiedBizObj.put(columnNames[p], row[p]);
									bizproc.BizProc.updateBizObj(modifiedBizObj);
									break;
								}
						}

						//TODO: notify the bizobj manager that an update has happened.
					}
				}
			}
		}
		protected string covertToString(object val)
		{
			return val == null ? "" : val.ToString();
		}

		public void processAjaxEvent(XmlElement eventInfo)
		{
			if (eventInfo.GetAttribute("event").CompareTo("bizObjSelected") == 0)
			{
				string bizObjId = eventInfo.GetAttribute("value");
				ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
				bizproc.BizProc.selectBizObj(bizObjId,this);
			}
			else if (eventInfo.GetAttribute("event").CompareTo("bizObjUpdated") == 0)
			{
				ActiveBizProc bizproc = BPC.getActiveBizProc(State.ParentBizProcIds[0]);
				m_bizobjList = bizproc.BizProc.SourceBizObjs;
				processClientMods();
			}

		}

		#endregion


		protected static string s_ajaxUrl = "/NitiboLiveStreamHandler.aspx?pi={0}&bpi={1}&cl={2}";
		protected static string s_perGridScript = @"
var doneCheckingRowSelect{0} = false;

function setupScroll{0}(gridId)
{{
	var grid = nitobi.getGrid(gridId);										   
	grid.vScrollbar.subscribe('ScrollByUser', updateScroll{0});
	grid.hScrollbar.subscribe('ScrollByUser', updateScroll{0});
}}

function updateScroll{0}(a,b)
{{
	var grid = nitobi.getGrid('{1}');		
	document.getElementById('{2}').value = grid.vScrollbar.getScrollPercent() + ',' + grid.hScrollbar.getScrollPercent();
}}

function setupGridState{0}(gridId,again)
{{
	var grid = nitobi.getGrid(gridId);
	if(grid.DoneSetupAlready)
		return;
	if(!again)
	{{
		var clientCtl = null;
		for(var pos = 0; pos < {4}BizProc.ClientControls.length; pos++)
			if({4}BizProc.ClientControls[pos].id == '{5}')
			{{
				clientCtl = {4}BizProc.ClientControls[pos];
				break;
			}}
		if(clientCtl == null)
		{{
			clientCtl = new ClientControl(nitobi.getGrid('{1}'), setGridState, getGridState);
			clientCtl.id = '{5}';
			{4}BizProc.addClientControl(clientCtl);
			nitobi.getGrid('{1}').ClientControl = clientCtl;
			nitobi.getGrid('{1}').BizProc = {4}BizProc;
			nitobi.getGrid('{1}').setGridResizeEnabled(true);
		}}
		clientCtl.selRowCtl = document.getElementById('{3}');
		clientCtl.xt = '{6}';
		clientCtl.ls = '{7}';
		grid.processGridSelected(document.getElementById('{3}').value);
		doneCheckingRowSelect{0} = true;
		grid.setupGridScrollPosition(document.getElementById('{2}').value);
		setTimeout(""setupGridState{0}('"" + gridId + ""',true)"", 200);
	}}
	else
	{{
		grid.finalizeRowCount({8});
//		grid.datatable.estimateRowCount = false;
//		grid.datatable.descriptor.estimateRowCount = false;
//		grid.datatable.descriptor.estimatedRowCount = {8};
		grid.DoneSetupAlready = true;
		grid.setupGridScrollPosition(document.getElementById('{2}').value);
		setTimeout(""setupScroll{0}('"" + gridId + ""')"", 400);
	}}
}}

function rowSelected{0}()
{{
	var oldSel = document.getElementById('{3}').value;
	rowFocus{0}('{1}');
	if(oldSel != document.getElementById('{3}').value)
		{4}BizProc.triggerEvent('{5}', 'bizObjSelected', document.getElementById('{3}').value);
}}

function rowFocus{0}(gridId)
{{
	var grid = nitobi.getGrid(gridId);
	var info = '';
	var rows = grid.getSelectedRows();
	if(rows != null && rows.length > 0)
	{{
		for(var p = 0; p < rows.length;p++)
			info += grid.getCellValue(nitobi.grid.Row.getRowNumber(rows[p]), 0) + ',';
		info = info.substring(0, info.length-1);
		document.getElementById('{3}').value = info;
	}}
}}
function saveCallback(eventArgs)
{{
	__doPostBack('{0}','save');
}}

";

		protected bool m_createOnLoad = true;
		#region INitobiClientControl Members

		public bool CreateOnLoad
		{
			set { m_createOnLoad = value; }
		}

		#endregion

		public override BizProcWidgetType BizProcWidgetType
		{
			get
			{
				return BizProcWidgetType.list;
			}
			set
			{
			}
		}

	}

}
